Fork me on GitHub
杨小慧的博客

JavaScript中的call()和apply()方法

JavaScript中的call()apply()都是用于调用当前函数functionObject,并可同时使用指定对象thisObj作为本次执行时functionObject函数内部的this指针引用。

它们都输属于Function对象,所有主流浏览器均支持。

1. call()和apply()的定义

1.1 call()方法

定义:调用一个对象的一个方法,以另一个对象替换当前对象。

语法
functionObject.call( [ thisObj [, arg1 [, arg2 [, args...]]]] )

参数

参数 描述
thisObj 可选/Object类型,指定执行functionObject函数时,函数内部this指针引用的对象。
arg1 可选/任意类型,调用functionObject函数时传入的第1个参数。
arg2 可选/任意类型,调用functionObject函数时传入的第2个参数。
args 可选/任意类型,调用functionObject函数时传入的更多参数,可以有多个。

1.2 apply()方法

定义:应用一个对象的一个方法,用另一个对象替换当前对象。

语法
functionObject.apply( [ thisObj [, argsArray ]] )

参数

参数 描述
thisObj 可选/Object类型,指定执行functionObject函数时,函数内部this指针引用的对象。
argsArray 可选/Array或arguments对象,调用functionObject时传入的参数数组或arguments对象。

1.3 call()和apply()的异同

相同点:

都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。

不同点

  • call():则是直接的参数列表,主要用在js对象各方法互相调用的时候,使当前this实例指针保持一致,或在特殊情况下需要改变this指针。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj
  • apply():最多只能有两个参数———新this对象和一个数组argsArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里面。如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。如果没有提供argsArraythisObj任何一个参数,那么Global对象将被用作thisObj,并且无法被传递任何参数。

总结:

  • call()apply()都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。
  • call()函数是将FunctionObject的参数一个个分别传入,如functionObject.call(thisObj,arg1,arg2,arg3)
  • apply()函数是将FunctionObject对象的参数以一个数组或arguments对象的形式整体传入,如functionObject.apply(func1,[arg1,arg2,arg3])

2. call()和apply()实例

2.1 实例一

1
2
3
4
5
6
7
8
9
10
function add(a, b) {
alert(a + b);
}
function sub(a, b) {
alert(a - b);
}
add.call(sub, 3,1);
//add.apply(sub, [3,1]);

说明:

例子中用add来替换subadd.call(sub,3,1) == add(3,1),所以运行结果为:4;

需要注意的是,这里直接用了函数名sub,而不是sub(),因为在js中的函数其实是对象,函数名是对Function 对象的引用。

2.1 实例二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Animal(name) {
this.name = name;
this.showName = function() {
alert(this.name);
}
}
function Cat(name) {
this.name = "Cat";
}
var animal = new Animal();
var cat = new Cat();
animal.showName.call(cat,",")
//animal.showName.apply(cat,[]);

说明

通过callapply方法,将原本属于Animal对象的showName()方法交给对象cat来使用了。因此输入结果为"Cat"

2.3 实例三(实现继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Animal(name) {
this.name = name;
this.showName = function() {
alert(this.name);
}
}
function Cat(name) {
Animal.call(this, name);
//Animal.apply(this, [name]);
}
var cat = new Cat("Black Cat");
cat.showName();

说明:

Animal.call(this, name) 的意思就是使用 Animal对象代替this对象,那么Cat对象就能够直接调用Animal的方法以及属性了。

2.4 实例四(多重继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Class10() {
this.showSub = function(a,b) {
alert(a-b);
}
}
function Class11() {
this.showAdd = function(a,b) {
alert(a+b);
}
}
function Class2() {
Class10.call(this);
Class11.call(this);
//Class10.apply(this);
//Class11.apply(this);
}
var c2=new Class2();
c2.showSub(3,1);
c2.showAdd(3,1); //使用两个 call 就实现多重继承

3. apply()的其他妙用

既然apply和call的用法差不多,那么为什么还同时存在呢?

——这是因为apply所传的参数类型(数组)还有其他妙用。

3.1 Math.max可以实现得到数组中最大的一项

因为Math.max参数不支持数组传递,但是它支持列表传递,即Math.max(param1,param2,param3…),所以可以根据apply的特点来解决 var max=Math.max.apply(null,array),这样轻易的可以得到一个数组中最大的一项。(apply会将一个数组转换为一个参数接一个参数的传递给方法)。

注意:调用的时候第一个参数给了一个null,是因为没有对象去调用这个方法,只需要用这个方法帮助运算,得到返回的结果就行,所以直接传递了一个null过去。

3.2 Array.min 可以实现得到数组中最小的一项

同样和max是一个思想 var min=Math.min.apply(null,array)

3.3 Array.prototype.push 可以实现两个数组合并

同样push方法没有提供push一个数组,但是它提供了push(param1,param,…paramN) 所以同样也可以通过apply来转换一下这个数组,即:

1
2
3
var arr1=new Array("1","2","3");
var arr2=new Array("4","5","6");
Array.prototype.push.apply(arr1,arr2);

也可以这样理解,arr1调用了push方法,参数是通过apply将数组装换为参数列表的集合。

总结:

当目标函数只需要n个参数列表,而不接收一个数组的形式([param1[,param2[,…[,paramN]]]]),可以通过apply的方式巧妙地解决这个问题。

------本文结束感谢阅读------